package cz.data.common.mark.manage;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import cz.data.common.database.utils.DbDialectUtils;
import cz.data.common.exception.DataException;
import cz.data.common.support.db.DbType;
import cz.data.common.support.db.core.DbColumn;
import cz.data.common.support.db.core.DbTable;
import cz.data.common.support.metadata.MysqlDataTypeEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;

import java.time.LocalDate;
import java.util.Date;
import java.util.Properties;

public class MySQLManageDialect implements JdbcManageDialect {

    @Override
    public String properties(DbTable table, Properties properties) {
        String tableModel = properties.getProperty("table_model", TableModel.Table.value);
        String partition = properties.getProperty("partition");
        StringBuilder sb = new StringBuilder();
        if (StringUtils.isNotBlank(partition)) {
            DbColumn partitionColumn = JdbcManageDialect.super.findColumn(table.getColumns(), partition);
            partition = DbDialectUtils.fieldAddQuoting(DbType.MYSQL.getDb(), partition);
            if (TableModel.RangePartition.equalsValue(tableModel)) {
                if (MysqlDataTypeEnum.DATE.equalsValue(partitionColumn.getDataType())
                        || MysqlDataTypeEnum.DATETIME.equalsValue(partitionColumn.getDataType())) {
                    String partitionModel = properties.getProperty("partition_model", PartitionModel.Year.value);
                    if (PartitionModel.Year.equalsValue(partitionModel)) {
                        int partitionNumber = 5;//五年
                        sb.append(" PARTITION BY RANGE(YEAR(").append(partition).append("))(");
                        int years = LocalDate.now().getYear();
                        for (int i = 0; i <= partitionNumber; i++) {
                            sb.append("PARTITION p").append(i).append(" VALUES LESS THAN(").append(years + i).append("),");
                        }
                        sb.append("PARTITION p_max_value VALUES LESS THAN MAXVALUE )");
                    } else if (PartitionModel.Month.equalsValue(partitionModel)) {
                        int number = 12 * 3;//三年
                        Date date = new Date();
                        String expr = "MONTH(" + partition + ") * 100 + DAYOFMONTH(" + partition + ")";
                        sb.append(" PARTITION BY RANGE(").append(expr).append(")(");
                        for (int i = 0; i < number; i++) {
                            String format = DateFormatUtils.format(DateUtils.addMonths(date, i), "yyyyMM");
                            sb.append("PARTITION p").append(i).append(" VALUES LESS THAN(").append(format).append("),");
                        }
                        sb.append("PARTITION p_max_value VALUES LESS THAN MAXVALUE )");
                    }
                } else {
                    throw new DataException("范围分区需要指定分区字段为时间类型");
                }
            } else if (TableModel.HashPartition.equalsValue(tableModel)) {
                String number_of_partition = properties.getProperty("number_of_partition", "10");
                int partitionNumber = NumberUtils.toInt(number_of_partition, 10);
                sb.append(" PARTITION BY LINEAR KEY(").append(partition).append(")");
                sb.append(" PARTITIONS ").append(partitionNumber).append(" ");
            }
        }
        return sb.toString();
    }

    @Getter
    @AllArgsConstructor
    public enum TableModel {
        Table("1"),
        RangePartition("2"),
        HashPartition("3"),
        ;

        private final String value;

        public boolean equalsValue(String value) {
            return this.value.equals(value);
        }
    }

    @Getter
    @AllArgsConstructor
    public enum PartitionModel {
        Year("year"),
        Month("month"),
        Day("day");

        private final String value;

        public boolean equalsValue(String value) {
            return this.value.equals(value);
        }
    }
}
